// list nodes matching this expression, optionally relative to the node `root'
function $x( xpath, root ) {
	var doc = root ? root.evaluate ? root : root.ownerDocument : document, next;
	var got = doc.evaluate( xpath, root||doc, null, 0, null ), result = [];
	switch (got.resultType) {
		case got.STRING_TYPE:
			return got.stringValue;
		case got.NUMBER_TYPE:
			return got.numberValue;
		case got.BOOLEAN_TYPE:
			return got.booleanValue;
		default:
			while (next = got.iterateNext())
				result.push( next );
				return result;
	}
}

function $X( xpath, root ) {
	var got = $x( xpath, root );
	return got instanceof Array ? got[0] : got;
}

function $(id) {
	return document.getElementById(id);
}

function trim(str) {
	return str.replace(/^\s+|\s+$/g, "");
}

function pluck(a, prop) {
  return a.map(function(i) { return i[prop]; });
}

function alpha(a,b) {
	return (a < b) ? -1 : ((a > b) ? 1 : 0);
}

function isArray(a) { return isObject(a) && isNumber(a.length); }
function isString(s) { return "string" == typeof s; }
function isNumber(n) { return "number" == typeof n; }
function isObject(o) { return "object" == typeof o; }
function isBoolean(b) { return "boolean" == typeof b; }
function isDefined(v) { return "undefined" != typeof v; }
function isUndefined(u) { return "undefined" == typeof u; }
function isFunction(f) { return "function" == typeof f; }

function array(a) { return isArray(a) ? a : [a]; }

function css(rules, disabled) {
	var head = document.documentElement.firstChild;
	var style = document.createElement("style");
	style.type = "text/css";
	style.textContent = isString(rules) ? rules : rules.toString();
	if (isBoolean(disabled))
		style.disabled = disabled;
	return head && head.appendChild(style);
}

XML.setSettings({
	ignoreProcessingInstructions: false,
	ignoreWhitespace: false,
	ignoreComments: false,
	prettyPrinting: false, prettyIndent: 2
});

function number(n) {
	if (isNumber(n)) return n;
	if (isObject(n))
		if (/input/i.test(n.nodeName||""))
			n = n.value;
		else if (n.textContent)
			n = n.textContent;
	n = n.replace(/[^\d.-]+/g, "");
	return n ? parseFloat(n) : undefined;
}

function integer(n) {
	if (isNumber(n)) return n;
	if (isObject(n))
		if (/input/i.test(n.nodeName||""))
			n = n.value;
		else if (n.textContent)
			n = n.textContent;
	if (isString(n))
		n = n.replace(/[^\d-]+/g, "");
	return n ? parseInt(n, 10) : undefined;
}

function urlParse(param, url) {
	if (!url) url = location.search || "";
	var keys = {};
	url.replace(/([^=&?]+)=([^&#]*)/g, function(m, key, value) {
		keys[decodeURIComponent(key)] = decodeURIComponent(value);
	});
	return (param ? keys[param] : keys) || "view" == param && document.body.id;
}

function clickTo(div, action, condition, capture, event, when) {
  if (isArray(node)) return map(clickTo, arguments);
  if (div) {
    div.addEventListener(event || "click", function(e) {
      if (e.button || when && !when(e)) return;
      if (!condition || $X(condition, e.target)) {
        e.stopPropagation();
        e.preventDefault();
        if (isFunction(action))
          action(e);
        else
          goto(action);
      }
    }, !!capture);
    div.style.cursor = "pointer";
  }
}

/*function createElement(type, attributes){
	var node = document.createElement(type);
	for (var attr in attributes) if (attributes.hasOwnProperty(attr)){
		node.setAttribute(attr, attributes[attr]);
	}
	return node;
}*/

var expandos = { id: 1, className: 1, title: 1, type: 1, checked: 1 };

function node(opt) {
	function attr(name) {
		var value = opt[name];
		delete opt[name];
		return value;
	}
	var id = opt.id;
	var n = $(id);
	if (!n) {
		var tag = attr("tag") || "div";
		if (isString(tag))
			n = document.createElement(tag);
		else if (!tag.toXMLString)
			n = tag;
		else {
			var t = document.createElement("div");
			t.innerHTML = tag.toXMLString();

			var ids = {};
			for each (var n in $x('.//*[@id]', t))
				ids[n.id] = 1;
			if (!n) ids = null;

			var r = document.createRange();
			r.selectNodeContents(t);
			n = r.extractContents();
			if (n.childNodes.length == 1)
				n = n.firstChild;
		}
		var after = attr("after"), replace = attr("replace");
		var before = opt.prepend ? opt.prepend.firstChild : attr("before");
		var parent = attr("prepend") || attr("append") || (before || after || replace || {}).parentNode;
		if (parent) {
			if (before)
				parent.insertBefore(n, before);
			else if (after)
				parent.insertBefore(n, after.nextSibling);
			else if (replace)
				parent.replaceChild(n, replace);
			else
				parent.appendChild(n);
		}
		if (id) n.id = id;
	}
	var html = attr("html");
	if (isDefined(html)) n.innerHTML = html;
	var text = attr("text");
	if (isDefined(text)) n.textContent = text;
	var style = attr("style");
	if (style)
		for (var prop in style)
			n.style[prop] = style[prop];
	for (prop in opt)
		if (expandos[prop])
			n[prop] = opt[prop];
		else
			n.setAttribute(prop, opt[prop]+"");
	if (ids)
		for (var id in ids)
			ids[id] = $(id);
	return ids || n;
}

// returns a function that only runs expensive function fn after no call to it
// has been made for n ms, or 100, if not given, or the time fn took last time,
// if it has been run at least once and no n was given.
function expensive(fn, n) {
	function run() {
		fn.timeout = null;
		var time = n || new Date;
		fn();
		if (!n) duration = (new Date) - time;
	}
	var duration;
	return function() {
		if (fn.timeout)
			clearTimeout(fn.timeout);
		fn.timeout = setTimeout(run, n || duration || 100);
	};
}
